/*
 * RingBuffer.cpp
 *
 *  Created on: Jun 28, 2024
 *      Author: root
 */

#include "RingBuffer.h"

namespace bsp {

uint32_t RingBuffer::getReadableSize(uint32_t *size1, uint32_t *size2) {
	uint32_t copy1, copy2;
	const uint32_t N = buffer.size();
	if (write_pos > read_pos) {
		copy1 = write_pos - read_pos;
		copy2 = 0;
	} else if (read_pos > write_pos) {
		copy1 = N - read_pos;
		copy2 = write_pos;
	} else {
		copy1 = copy2 = 0;
	}

	if (size1)
		*size1 = copy1;
	if (size2)
		*size2 = copy2;

	return copy1 + copy2;
}

void RingBuffer::append(const char *data, uint32_t len) {
	ASSERT(data && len > 0, "data not null and len > 0");
	const uint32_t N = buffer.size();
	uint32_t copy1 = N - write_pos, copy2 = 0;

	if (len > copy1) {
		copy2 = (len - copy1) % N;
	} else {
		copy1 = len;
	}

	auto orginal_write_pos = write_pos;
	if (copy1 > 0) {
		memcpy(&buffer[0] + write_pos, data, copy1);
		write_pos = (write_pos + copy1) % N;
	}
	if (copy2 > 0) {
		memcpy(&buffer[0], data + copy1, copy2);
		write_pos = copy2 % N;
	}

	if ((len >= N) || (orginal_write_pos < read_pos && write_pos >= read_pos)
			|| (orginal_write_pos > read_pos && write_pos >= read_pos
					&& write_pos <= orginal_write_pos)) {
		overwrite_count++;
	}
}

uint32_t RingBuffer::retrieve(char *data, uint32_t size) {
	ASSERT(data && size > 0, "data not null and size must > 0");
	const uint32_t N = buffer.size();
	uint32_t copy1, copy2;
	if (getReadableSize(&copy1, &copy2) > 0) {
		copy1 = std::min(size, copy1);
		copy2 = std::min(copy2, (uint32_t) (size - copy1));
	}

	if (copy1) {
		std::copy(&buffer[0] + read_pos, &buffer[0] + read_pos + copy1, data);
		read_pos = (read_pos + copy1) % N;
	}

	if (copy2) {
		std::copy(&buffer[0], &buffer[0] + copy2, data + copy1);
		read_pos = copy2 % N;
	}

	return copy1 + copy2;
}

std::string RingBuffer::retrieve(uint32_t size) {
	ASSERT(size > 0, "size must > 0");
	const uint32_t N = buffer.size();
	uint32_t copy1, copy2;
	if (getReadableSize(&copy1, &copy2) > 0) {
		copy1 = std::min(size, copy1);
		copy2 = std::min(copy2, (uint32_t) (size - copy1));
	}

	std::string str = "";
	if (copy1) {
		str.append(&buffer[0] + read_pos, &buffer[0] + read_pos + copy1);
		read_pos = (read_pos + copy1) % N;
	}

	if (copy2) {
		str.append(&buffer[0], &buffer[0] + copy2);
		read_pos = copy2 % N;
	}

	return str;
}

int RingBuffer::readFd(int fd) {
	// total size
	return 0;
}

int RingBuffer::writeFd(int fd) {
	return 0;
}

void RingBuffer::test1() {
	{
		RingBuffer bf(5);
		bf.append("0", 1);
		bf.append("1", 1);
		bf.append("2", 1);
		bf.append("3", 1);
		ASSERT(bf.retrieve() == "0123", "");
		ASSERT(bf.getReadPos() == 4, "");
		ASSERT(bf.getWritePos() == 4, "");
	}

	{
		RingBuffer bf(5);
		bf.append("0", 1);
		bf.append("1", 1);
		bf.append("2", 1);
		bf.append("3", 1);
		bf.append("4", 1);
		ASSERT(bf.retrieve() == "", "");
		ASSERT(bf.getReadPos() == 0, "");
		ASSERT(bf.getReadPos() == bf.getWritePos(), "");
	}

	{
		RingBuffer bf(5);
		bf.append((const char*) "01234", 5);
		ASSERT(bf.retrieve() == "", "");
		ASSERT(bf.getReadPos() == 0, "");
		ASSERT(bf.getReadPos() == bf.getWritePos(), "");
	}

	{
		RingBuffer bf(5);
		bf.append("0123456", 7);
		ASSERT(bf.getWritePos() == 2, "");
		ASSERT(bf.retrieve() == "56", "");
		ASSERT(bf.getReadPos() == 2, "");
	}
}

void RingBuffer::test2() {
	{
		RingBuffer bf(30);
		for (int i = 0; i < 100; i++) {
			bf.append("111111", 4);
//			ASSERT(bf.getOverwriteCount() == 4 * (i+1) / 30, "");
//			std::cout<<bf.getWritableSize()<<std::endl;
		}
	}
}

} /* namespace bsp */
